package com.jplus.core.mvc;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.InetAddress;
import java.net.SocketException;
import java.net.URLDecoder;
import java.util.Enumeration;
import java.util.LinkedHashMap;
import java.util.Map;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.alibaba.fastjson.JSON;
import com.jplus.core.core.CharSet;
import com.jplus.core.util.FormatUtil;
import com.jplus.core.util.StreamUtil;

/**
 * Web 操作工具类,依赖于DataContext
 */
public class WebUtil {
	private static final Logger logger = LoggerFactory.getLogger(WebUtil.class);

	/**
	 * 将数据以 JSON 格式写入响应中
	 */
	public static void writeJSON(Object data) {
		write("application/json", FormatUtil.toJSONString(data));
	}

	/**
	 * 将数据以 HTML 格式写入响应中
	 */
	public static void writeHTML(String data) {
		write("text/html", data);
	}

	/**
	 * 将数据以 TEXT 格式写入响应中
	 */
	public static void writeTEXT(String data) {
		write("text/plain", data);
	}

	public static void write(String ContentType, String data) {
		try {
			HttpServletResponse response = DataContext.getResponse();
			// 设置响应头
			response.setContentType(ContentType); // 指定内容类型为 HTML 格式
			response.setCharacterEncoding(CharSet.Default); // 防止中文乱码
			// 向响应中写入数据
			PrintWriter writer = response.getWriter();
			writer.write(data);
			writer.flush();
			writer.close();
		} catch (Exception e) {
			logger.error("在响应中写数据出错！", e);
			throw new RuntimeException(e);
		}
	}

	/**
	 * 从请求中获取所有参数（当参数名重复时，用后者覆盖前者）
	 */
	public static <T> T getRequestParamBean(Class<T> clazz) {
		return JSON.parseObject(JSON.toJSONString(getRequestParamMap()), clazz);
	}

	/**
	 * 从请求中获取所有参数（当参数名重复时，用后者覆盖前者）
	 */
	public static Map<String, Object> getRequestParamMap() {
		Map<String, Object> paramMap = new LinkedHashMap<String, Object>();
		try {
			HttpServletRequest request = DataContext.getRequest();
			String method = request.getMethod();
			if (method.equalsIgnoreCase("put") || method.equalsIgnoreCase("delete")) {
				String queryString = URLDecoder.decode(StreamUtil.getString(request.getInputStream()), CharSet.Default);
				if (!FormatUtil.isEmpty(queryString)) {
					String[] qsArray = queryString.split("&");
					if (qsArray != null) {
						for (String qs : qsArray) {
							String[] array = qs.split("=");
							if (array != null && array.length == 2) {
								String paramName = array[0];
								String paramValue = array[1];
								if (checkParamName(paramName)) {
									if (paramMap.containsKey(paramName)) {
										paramValue = paramMap.get(paramName) + FormatUtil.SEPARATOR + paramValue;
									}
									paramMap.put(paramName, paramValue);
								}
							}
						}
					}
				}
			} else {
				Enumeration<String> paramNames = request.getParameterNames();
				while (paramNames.hasMoreElements()) {
					String paramName = paramNames.nextElement();
					if (checkParamName(paramName)) {
						String[] paramValues = request.getParameterValues(paramName);
						if (paramValues != null) {
							if (paramValues.length == 1) {
								paramMap.put(paramName, paramValues[0]);
							} else {
								StringBuilder paramValue = new StringBuilder("");
								for (int i = 0; i < paramValues.length; i++) {
									paramValue.append(paramValues[i]);
									if (i != paramValues.length - 1) {
										paramValue.append(FormatUtil.SEPARATOR);
									}
								}
								paramMap.put(paramName, paramValue.toString());
							}
						}
					}
				}
			}
		} catch (Exception e) {
			logger.error("获取请求参数出错！", e);
			throw new RuntimeException(e);
		}
		return paramMap;
	}

	private static boolean checkParamName(String paramName) {
		return !paramName.equals("_"); // 忽略 jQuery 缓存参数
	}

	/**
	 * 转发请求
	 */
	public static void forwardRequest(String path) {
		try {
			HttpServletRequest request = DataContext.getRequest();
			HttpServletResponse response = DataContext.getResponse();
			path = FormatUtil.formatPath(path);
			request.getRequestDispatcher(path).forward(request, response);
		} catch (Exception e) {
			logger.error("转发请求出错！", e);
			throw new RuntimeException(e);
		}
	}

	/**
	 * 重定向请求
	 */
	public static void redirectRequest(String path) {
		try {
			HttpServletRequest request = DataContext.getRequest();
			HttpServletResponse response = DataContext.getResponse();
			path = request.getContextPath() + "/" + path;
			path = FormatUtil.formatPath(path);
			response.sendRedirect(path);
		} catch (Exception e) {
			logger.error("重定向请求出错！", e);
			throw new RuntimeException(e);
		}
	}

	/**
	 * 发送错误代码
	 */
	public static void sendError(int code, String message) {
		try {
			HttpServletResponse response = DataContext.getResponse();
			response.sendError(code, message);
		} catch (Exception e) {
			logger.error("发送错误代码出错！", e);
			throw new RuntimeException(e);
		}
	}

	/**
	 * 判断是否为 AJAX 请求
	 */
	public static boolean isAJAX() {
		return DataContext.getRequest().getHeader("X-Requested-With") != null;
	}

	/**
	 * 获取请求路径
	 */
	public static String getRequestPath() {
		HttpServletRequest request = DataContext.getRequest();
		String servletPath = request.getServletPath();
		String pathInfo = FormatUtil.toString(request.getPathInfo());
		return servletPath + pathInfo;
	}

	/**
	 * 从 Cookie 中获取数据
	 */
	public static String getCookie(String name) {
		String value = "";
		try {
			HttpServletRequest request = DataContext.getRequest();
			Cookie[] cookieArray = request.getCookies();
			if (cookieArray != null) {
				for (Cookie cookie : cookieArray) {
					if (!FormatUtil.isEmpty(name) && name.equals(cookie.getName())) {
						value = URLDecoder.decode(cookie.getValue(), CharSet.Default);
						break;
					}
				}
			}
		} catch (Exception e) {
			logger.error("获取 Cookie 出错！", e);
			throw new RuntimeException(e);
		}
		return value;
	}

	/**
	 * 下载文件
	 */
	public static void downloadFile(String filePath) {
		try {
			HttpServletResponse response = DataContext.getResponse();
			String path = FormatUtil.formatPath(filePath);
			String originalFileName = path.substring(path.lastIndexOf("/"));
			String downloadedFileName = new String(originalFileName.getBytes("GBK"), "ISO8859_1"); // 防止中文乱码

			response.setContentType("application/octet-stream");
			response.addHeader("Content-Disposition", "attachment;filename=\"" + downloadedFileName + "\"");

			InputStream inputStream = new BufferedInputStream(new FileInputStream(filePath));
			OutputStream outputStream = new BufferedOutputStream(response.getOutputStream());
			StreamUtil.copyStream(inputStream, outputStream);
		} catch (Exception e) {
			logger.error("下载文件出错！", e);
			throw new RuntimeException(e);
		}
	}

	/**
	 * 设置 Redirect URL 到 Session 中
	 */
	public static void setRedirectUrl(String sessionKey) {
		if (!isAJAX()) {
			String requestPath = getRequestPath();
			DataContext.getSession().setAttribute(sessionKey, requestPath);
		}
	}

	/**
	 * 是否为 IE 浏览器
	 */
	public boolean isIE() {
		String agent = DataContext.getRequest().getHeader("User-Agent");
		return agent != null && agent.contains("MSIE");
	}

	/**
	 * 本机IP
	 */
	public static String getLocalRealIp() {
		try {
			return InetAddress.getLocalHost().getHostAddress().toString();
		} catch (Exception e) {
			logger.error("获取本机IP异常", e);
			return "127.0.0.1";
		}
	}

	/**
	 * 
	 * @param request
	 * @return 请求IP地址
	 */
	public static String getRequstIp() {
		String ip = null;
		HttpServletRequest request = DataContext.getRequest();
		ip = request.getHeader("X-Forwarded-For");
		if (isRealIP(ip))
			return getRealIp(ip);
		ip = request.getHeader("Proxy-Client-IP");
		if (isRealIP(ip))
			return getRealIp(ip);
		ip = request.getHeader("WL-Proxy-Client-IP");
		if (isRealIP(ip))
			return getRealIp(ip);
		ip = request.getHeader("HTTP_CLIENT_IP");
		if (isRealIP(ip))
			return getRealIp(ip);
		ip = request.getHeader("HTTP_X_FORWARDED_FOR");
		if (isRealIP(ip))
			return getRealIp(ip);
		ip = request.getParameter("__fromReferIP");
		if (isRealIP(ip))
			return getRealIp(ip);
		ip = request.getHeader("X-Real-IP");// NGINX用
		if (isRealIP(ip))
			return getRealIp(ip);
		ip = request.getRemoteAddr();
		return ip;
	}

	private static boolean isRealIP(String ip) {
		return !FormatUtil.isEmpty(ip) && !"unknown".equalsIgnoreCase(ip);
	}

	private static String getRealIp(String ip) {
		if (String.valueOf(ip).indexOf(",") != -1) {
			return FormatUtil.getLeft(ip.split(",")[0], 15);
		}
		return ip;
	}
}
